package ee.ut.maxorato_macobo.oop.mud.game;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;

import ee.ut.maxorato_macobo.oop.mud.game.GameCommandSystem.GameCommand;
import ee.ut.maxorato_macobo.oop.mud.items.ContainsItems;
import ee.ut.maxorato_macobo.oop.mud.items.ItemBase;
import ee.ut.maxorato_macobo.oop.mud.items.ItemCorpse;
import ee.ut.maxorato_macobo.oop.mud.items.ItemTemplate;
import ee.ut.maxorato_macobo.oop.mud.items.ItemTemplatePool;
import ee.ut.maxorato_macobo.oop.mud.network.NetSession;

public class PlayerCharacter extends GameCharacter {
	private NetSession session;
	private String playerName;
	private LinkedList<GameCommand> commandQueue;
	private int delayTicks;
	private StringBuffer tickMessage;
	private boolean promptForced;
	private int maxMana;
	private int maxEnergy;
	private int maxHealth;
	private boolean inputSincePrompt;
	private int currentLifeCycle;
	
	public PlayerCharacter(NetSession newSession, String newPlayerName) {
		super(null);
		
		currentLifeCycle = 1;
		
		session = newSession;
		setPlayerName(newPlayerName);
		
		tickMessage = new StringBuffer("");
		
		commandQueue = new LinkedList<GameCommand>();
		delayTicks = 0;
		
		inputSincePrompt = false;
		promptForced = false;
		
		maxEnergy = 100;
		maxMana = 100;
		maxHealth = 100;
		
		currentEnergy = getMaxEnergy();
		currentMana = getMaxMana();
		currentHealth = getMaxHealth();
		
		if(GameArea.getStartRoom() != null) {
			moveToRoom(GameArea.getStartRoom(), "", true);
		}
		
		if(currentRoom != null) {
			currentRoom.characterEnteredRoom(this);
		}
	}
	
	public void describeForPlayer(PlayerCharacter player) {
		player.sendTickMessage("Player '" + getPlayerName() + "' with session ID " + getSessionId() + ".");
		
		player.sendTickMessage("This player is carrying the following items: ");
		GameUtils.describeItemListForPlayer(player, getItemList(), false);
	}
	
	public void attemptFlee() {
		validateFight();
		
		if(fightTarget == null) {
			sendTickMessage("You are not in a fight.");
		}
		else {
			int percentile = randGen.nextInt(1001);
			
			if(percentile < 700) {
				sendTickMessage("You were not able to flee.");
			}
			else {
				ArrayList<String> exitDirs = getRoom().getExitList();
				
				if(exitDirs.size() == 0) {
					sendTickMessage("You cannot flee from this room.");
				}
				else {
					sendTickMessage("You managed to flee.");
					validateFight();
					
					GameRoom oldRoom = getRoom();
					
					GameRoom newRoom = getRoom().getExitAtDir(exitDirs.get(randGen.nextInt(exitDirs.size())));
					
					if(newRoom == oldRoom) {
						sendTickMessage("But somehow you ended up in the same place.");
					}
					else {
						moveToRoom(newRoom, "void");
						
						if(oldRoom != newRoom) {
							for(PlayerCharacter player : oldRoom.getRoomPlayerList()) {
								player.sendTickMessage(getCharName() + " fled from a fight.");
							}
						}
					}
				}
			}
		}
	}
	
	public void handleDeath(boolean defaultNotice) {
		fightTarget = null;
		
		if(defaultNotice) {
			for(PlayerCharacter player : currentRoom.getRoomPlayerList()) {
				if(player == this) player.sendTickMessage("You died.");
				else player.sendTickMessage("Player " + getCharName() + " died.");
			}
		}
		
		ItemTemplate corpseTemplate = ItemTemplatePool.findByUniqueName("corpse");
		
		if(corpseTemplate != null) {
			ItemBase corpse = corpseTemplate.createInstance(currentRoom);
			
			if(corpse instanceof ContainsItems) {
				ArrayList<ItemBase> itemList = new ArrayList<ItemBase>(getItemList());
				
				for(ItemBase item : itemList) {
					item.moveToLocation((ContainsItems)corpse);
				}
			}
			
			if(corpse instanceof ItemCorpse) {
				((ItemCorpse)corpse).lockCorpse();
			}
			
			corpse.setCustomDescription("This corpse belongs to " + getPlayerName() + ".");
		}
		
		currentEnergy = getMaxEnergy();
		currentMana = getMaxMana();
		currentHealth = getMaxHealth();
		
		currentLifeCycle++;
		moveToRoom(GameArea.getStartRoom(), "void");
	}
	
	public void savePlayer() {
		
	}
	
	public int getNumberOfHits(GameCharacter target) {
		return randGen.nextInt(2) + 1;
	}
	
	public boolean isAlive(int lifeCycleIndex) {
		return (lifeCycleIndex >= currentLifeCycle); 
	}
	
	public int getLifeCycleIndex() {
		return currentLifeCycle;
	}
	
	public boolean hasAutoRetaliate() {
		return true;
	}
	
	public String getCharName() {
		return getPlayerName();
	}
	
	public int getMaxEnergy() {
		return maxEnergy;
	}
	
	public int getMaxMana() {
		return maxMana;
	}
	
	public int getMaxHealth() {
		return maxHealth;
	}
	
	public void addCommandToQueue(GameCommand command) {
		commandQueue.add(command);
	}
	
	public void setDelayTicks(int newDelayTicks) {
		delayTicks = newDelayTicks;
	}
	
	public void processCommandTick() {
		validateFight();
		
		if(delayTicks > 0) {
			delayTicks--;
		}
		else if(!commandQueue.isEmpty()) {
			GameCommand command = commandQueue.pop();
			command.execute();
		}
		
		if(!getIsSleeping()) {
			if(getHealth() < getMaxHealth() && randGen.nextInt(2) == 0) {
				currentHealth++;
			}
			
			if(getEnergy() < getMaxEnergy()) {
				currentEnergy++;
			}
			
			if(getMana() < getMaxMana()) {
				currentMana++;
			}
		}
		else {
			if(getHealth() < getMaxHealth() && randGen.nextInt(1) == 0) {
				currentHealth += 1;
			}
			
			if(getEnergy() < getMaxEnergy()) {
				currentEnergy = Math.min(getMaxEnergy(), currentEnergy + 3);
			}
			
			if(getMana() < getMaxMana()) {
				currentMana = Math.min(getMaxMana(), currentMana + 2);
			}
		}
	}
	
	public void processOutputTick() {
		String toSend = "";
		
		if(tickMessage.length() > 0) {
			if(!inputSincePrompt) toSend += "\r\n";
			toSend += tickMessage.toString() + "\r\n";
		}
		
		if(tickMessage.length() > 0 || promptForced) {
			String prompt = "M:" + getMana() + "/" + getMaxMana() + " H:" + getHealth() + "/" + getMaxHealth() + "> "; 
			toSend += prompt;
			promptForced = false;
		}
		
		sendMessageRaw(toSend);
		
		tickMessage.delete(0, tickMessage.length());
		
		inputSincePrompt = false;
	}
	
	public void forcePrompt() {
		promptForced = true;
	}
	
	public void moveToRoom(GameRoom newRoom, String direction) {
		moveToRoom(newRoom, direction, true);
	}
	
	public void moveToRoom(GameRoom newRoom, String direction, boolean bSendDesc) {
		super.moveToRoom(newRoom, direction);
		
		if(bSendDesc) describeCurrentRoom();
	}
	
	public void describeCurrentRoom() {
		if(currentRoom != null) {
			sendTickMessage(currentRoom.getRoomName());
			sendTickMessage("\033[1;30m" + currentRoom.getRoomDesc() + "\033[0m\r\n");
			
			ListIterator<NpcCharacter> itn = currentRoom.getRoomNpcList().listIterator();
			
			sendTickMessage("\033[0;32m [Exits: " + currentRoom.getExitListString() + "]\033[0m");
			
			while(itn.hasNext()) {
				NpcCharacter nextPlayer = itn.next();
				
				sendTickMessage(nextPlayer.getBriefStatusDesc() + "\033[1;33m" + GameUtils.stringCapitalize(nextPlayer.getTemplate().getNpcDescription()) + " is looking at you.\033[0m");
			}
			
			ListIterator<PlayerCharacter> it = currentRoom.getRoomPlayerList().listIterator();
			
			while(it.hasNext()) {
				PlayerCharacter nextPlayer = it.next();
				
				if(nextPlayer != this) {
					sendTickMessage(nextPlayer.getBriefStatusDesc() + "\033[1;33m" + nextPlayer.getPlayerName() + " is standing here.\033[0m");
				}
			}
			
			GameUtils.describeItemListForPlayer(this, currentRoom.getItemList(), true);
		}
	}
	
	public void exitGame() {
		if(currentRoom != null) {
			currentRoom.playerLeftRoom(this, null);
		}
	}
	
	public void kickPlayer() {
		PlayerPool.removePlayer(this);
		session.terminateSession();
	}
	
	public void syncedKickPlayer() {
		if(!PlayerPool.getDisconnectList().contains(this)) {
			PlayerPool.getDisconnectList().add(this);
		}
	}
	
	public void inputText(String input) {
		inputSincePrompt = true;
		
		GameCommandSystem.processAsCommand(this, input);
	}
	
	public void sendMessage(String message) {
		session.sendOutput(message + "\r\n");
	}
	
	public void sendMessageRaw(String message) {
		session.sendOutput(message);
	}
	
	public void sendTickMessage(String message) {
		tickMessage.append(message + "\r\n");
	}

	public String getPlayerName() {
		return playerName;
	}

	public void setPlayerName(String playerName) {
		this.playerName = playerName;
	}
	
	public int getSessionId() {
		return session.getSessionId();
	}
	
}
